﻿using System;
using System.Data;
using NHibernate;
using NHibernate.Cfg;
using AcademicPlanner.Data.Repositories;

namespace AcademicPlanner.Data
{
	/// <summary>
	/// DataSourceAccessSupervisor is the front controller and master object
	/// of the data layer of the AcademicPlanner application.
	/// </summary>
	/// <version>1.0</version>
	/// <since>1.0</since>
	public class DataSourceAccessSupervisor
	{
		private ISessionFactory sessions;

		private ISession session;

		private ITransaction currentTransaction;

		private int transactionDepth = 0;


		/// <summary>
		/// Gets the current NHibernate session.
		/// </summary>
		/// <version>1.0</version>
		/// <since>1.0</since>
		internal ISession Session { get { return session; } }

		/// <summary>
		/// Gets the current database connection used by the data layer.
		/// </summary>
		/// <version>1.0</version>
		/// <since>1.0</since>
		public IDbConnection Connection { get { return session.Connection; } }

		/// <summary>
		/// Gets the repository responsible for <c>PersonEntity</c>s.
		/// </summary>
		/// <version>1.0</version>
		/// <since>1.0</since>
		public PersonEntityRepository PersonEntityRepo { get { return PersonEntityRepository.GetInstance(); } }

		/// <summary>
		/// Gets the repository responsible for <c>AccountEntity</c>s.
		/// </summary>
		/// <version>1.0</version>
		/// <since>1.0</since>
		public AccountEntityRepository AccountEntityRepo { get { return AccountEntityRepository.GetInstance(); } }

		/// <summary>
		/// Gets the repository responsible for <c>UserEntity</c>s.
		/// </summary>
		/// <version>1.0</version>
		/// <since>1.0</since>
		public UserEntityRepository UserEntityRepo { get { return UserEntityRepository.GetInstance(); } }


		private DataSourceAccessSupervisor() 
		{
			sessions = new Configuration().Configure().BuildSessionFactory();
			session = sessions.OpenSession();
		}

		private class InstanceHolder 
		{
			internal static DataSourceAccessSupervisor INSTANCE = new DataSourceAccessSupervisor(); 
		}


		/// <summary>
		/// Gets the single instance of DataSourceAccessSupervisor.
		/// </summary>
		/// <returns>The single instance of DataSourceAccessSupervisor.</returns>
		/// <version>1.0</version>
		/// <since>1.0</since>
		public static DataSourceAccessSupervisor GetInstance()
		{ 
			return InstanceHolder.INSTANCE; 
		}


		private void RenewSession()
		{
			session.Close();
			session = sessions.OpenSession();
		}

		/// <summary>
		/// Prepares the persistence for transaction oriented operations.
		/// </summary>
		/// <version>1.0</version>
		/// <since>1.0</since>
		public void PrepareForWork()
		{
			StartTransaction();
		}

		/// <summary>
		/// Cleans up the persistence after transaction oriented operations.
		/// Changes are maintained or reverted based on <paramref name="pSucceeded"/>.
		/// </summary>
		/// <param name="pSucceeded">
		/// Whether the transaction oriented operations to clean up after were successful or not.
		/// </param>
		/// <version>1.0</version>
		/// <since>1.0</since>
		public void CleanUpAfterWork(bool pSucceeded)
		{
			if (pSucceeded)
				CommitTransaction();
			else
				RollBackTransaction();
		}

		private void StartTransaction()
		{
			if (transactionDepth++ == 0)
				currentTransaction = session.BeginTransaction();
		}

		private void RollBackTransaction()
		{
			if (transactionDepth-- == 1)
			{
				currentTransaction.Rollback();
			}
		}

		private void CommitTransaction()
		{
			if (transactionDepth-- == 1)
			{
				currentTransaction.Commit();
			}
		}

	}
}
